#include "../include/Player.hh"
#include "../include/Item.hh"
#include <iostream>
#include <cstdlib>
#include <ctime>

namespace Game {
    // Default constructor
    Player::Player() 
        : name_("Player"), class_(WARRIOR), health_(100), 
          attack_(10), defense_(5), expToNextLevel_(100), 
          criticalChance_(0.1) {}

    // Parameterized constructor
    Player::Player(const std::string& name, Class playerClass)
        : name_(name), class_(playerClass), expToNextLevel_(100) {
        switch (playerClass) {
            case WARRIOR:
                health_ = 120; maxHealth_ = 120; attack_ = 15; defense_ = 10;
                break;
            case MAGE:
                health_ = 80; maxHealth_ = 80; attack_ = 20; defense_ = 5;
                break;
            case ROGUE:
                health_ = 100; maxHealth_ = 100; attack_ = 18; defense_ = 7; 
                criticalChance_ = 0.2;
                break;
        }
    }

    // Copy constructor
    Player::Player(const Player& other) 
        : name_(other.name_), class_(other.class_), 
          health_(other.health_), attack_(other.attack_),
          defense_(other.defense_), exp_(other.exp_), 
          expToNextLevel_(other.expToNextLevel_), level_(other.level_),
          criticalChance_(other.criticalChance_) {
        for (const auto& item : other.inventory_) {
            inventory_.push_back(std::make_unique<Item>(*item));
        }
    }

    // Assignment operator
    Player& Player::operator=(const Player& other) {
        if (this != &other) {
            name_ = other.name_;
            class_ = other.class_;
            health_ = other.health_;
            attack_ = other.attack_;
            defense_ = other.defense_;
            exp_ = other.exp_;
            expToNextLevel_ = other.expToNextLevel_;
            level_ = other.level_;
            criticalChance_ = other.criticalChance_;
            inventory_.clear();
            for (const auto& item : other.inventory_) {
                inventory_.push_back(std::make_unique<Item>(*item));
            }
        }
        return *this;
    }

    // ICharacter: Check if alive
    bool Player::isAlive() const { 
        return health_ > 0; 
    }

    // ICharacter: Take damage
    void Player::takeDamage(int damage) {
        if (damage < 0) {
            health_ -= damage; // 负数表示治疗
            if (health_ > maxHealth_) health_ = maxHealth_;
        } else {
            health_ = (damage > health_) ? 0 : health_ - damage;
        }
    }

    // ICharacter: Attack target
    void Player::attack(ICharacter& target) {
        unsigned damage = calculateDamage(target.getDefense());
        bool isCritical = (damage > (attack_ - target.getDefense()));
        target.takeDamage(damage);
        std::cout << name_ << " attacks! ";
        if (isCritical) std::cout << "(Critical!) ";
        std::cout << "Deals " << damage << " damage!\n";
    }

    // ICharacter: Name getter
    std::string Player::getName() const { 
        return name_; 
    }

    // ICharacter: Health getter
    unsigned Player::getHealth() const { 
        return health_; 
    }

    // ICharacter: Attack getter
    unsigned Player::getAttack() const { 
        return attack_; 
    }

    // ICharacter: Defense getter
    unsigned Player::getDefense() const { 
        return defense_; 
    }

    // ICharacter: Add item to inventory
    void Player::addItem(Item* item) {
        if (item) {
            inventory_.push_back(std::unique_ptr<Item>(item));
            std::cout << ">> Obtained " << item->getName() << "!\n";
        }
    }
    //get inventory
    const std::vector<std::unique_ptr<Item>>& Player::getInventory() const { 
        return inventory_; 
    }
    //get inventory size
    unsigned Player::getInventorySize() const { 
        return inventory_.size(); 
    }
    // ICharacter: Display inventory
    void Player::displayInventory() const {
        std::cout << "=== Inventory ===\n";
        for (size_t i = 0; i < inventory_.size(); ++i) {
            std::cout << i + 1 << ". " << inventory_[i]->getName() << "\n";
        }
    }

    // ICharacter: Generate drop (players cannot drop items)
    std::unique_ptr<Item> Player::generateDrop() const {
        return nullptr; 
    }

    // ICharacter: Clone player
    std::unique_ptr<ICharacter> Player::clone() const {
        return std::make_unique<Player>(*this);
    }

    // Gain EXP and level up if needed
    void Player::gainExp(unsigned exp) {
        exp_ += exp;
        while (exp_ >= expToNextLevel_ && level_ < 50) {
            levelUp();
        }
    }

    // Level up with attribute choice
    void Player::levelUp() {
        exp_ -= expToNextLevel_;
        expToNextLevel_ = static_cast<unsigned>(expToNextLevel_ * 1.5);

        system("cls");
        std::cout << "=== LEVEL UP! ===\n"
                  << "Current Level: " << level_ << "\n"
                  << "EXP to Next Level: " << expToNextLevel_ << "\n"
                  << "Choose an attribute to enhance:\n"
                  << "1. Max HP +20\n"
                  << "2. Attack +5\n"
                  << "3. Defense +3\n"
                  << "Choice: ";

        int choice;
        std::cin >> choice;

        switch (choice) {
            case 1: 
                health_ += 20;
                std::cout << ">> Max HP increased to " << health_ << "!\n";
                break;
            case 2: 
                attack_ += 5;
                std::cout << ">> Attack increased to " << attack_ << "!\n";
                break;
            case 3: 
                defense_ += 3;
                std::cout << ">> Defense increased to " << defense_ << "!\n";
                break;
            default:
                std::cout << ">> Invalid choice! HP increased by default.\n";
                health_ += 20;
        }

        level_++;
        std::cout << "\n=== New Stats ===\n"
                  << "HP: " << health_ << "\n"
                  << "ATK: " << attack_ << "\n"
                  << "DEF: " << defense_ << "\n";
        std::cout << "Press Enter to continue...";
        std::cin.ignore();
        std::cin.get();
    }

    // Show detailed stats
    void Player::showStats() const {
        std::cout << "=== STATS ===\n"
                  << "Name: " << name_ << "\n"
                  << "Level: " << level_ << "\n"
                  << "EXP: " << exp_ << "/" << expToNextLevel_ << "\n"
                  << "HP: " << health_ << "\n"
                  << "ATK: " << attack_ << "\n"
                  << "DEF: " << defense_ << "\n"
                  << "Crit Chance: " << (criticalChance_ * 100) << "%\n";
    }

    // Try to escape (30% success)
    bool Player::tryEscape() { 
        return (rand() % 100) < 30; 
    }

    // Use item by index
    void Player::useItem(int index) {
        if (index < 0 || index >= static_cast<int>(inventory_.size())) return;
        Item* item = inventory_[index].get();
        health_ += item->getHealthBoost();
        attack_ += item->getAttackBoost();
        defense_ += item->getDefenseBoost();
        std::cout << ">> Used " << item->getName() 
                  << " (HP+" << item->getHealthBoost()
                  << ", ATK+" << item->getAttackBoost()
                  << ", DEF+" << item->getDefenseBoost() << ")\n";
        inventory_.erase(inventory_.begin() + index);
    }

    // Calculate damage with crit chance
    unsigned Player::calculateDamage(unsigned targetDefense) const {
        bool isCritical = (rand() % 100) < (criticalChance_ * 100);
        unsigned finalAttack = isCritical ? attack_ * 2 : attack_;
        return (finalAttack > targetDefense) ? finalAttack - targetDefense : 0;
    }
}